/*
 * Copyright (c) 2008  Jonhnny Weslley
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
package easyaccept.maven;

import static java.util.Arrays.asList;
import static org.codehaus.plexus.util.StringUtils.join;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoFailureException;

/**
 * Helper class use to call a java Main in an external process.
 * 
 * @author Jonhnny Weslley
 */
public class JavaCommand {

    private final AbstractMojo mojo;

    private final List<String> env;
    private final List<String> args;
    private final List<String> jvmArgs;
    private final List<String> classpath;
    private String javaExec;
    private final String mainClassName;

    public JavaCommand(AbstractMojo aMojo, String aMainClassName) {
        javaExec = System.getProperty("java.home");
        if (javaExec == null) {
            javaExec = System.getenv("JAVA_HOME");
            if (javaExec == null) {
                throw new IllegalStateException("Couldn't locate java, try setting JAVA_HOME environment variable.");
            }
        }
        javaExec += File.separator + "bin" + File.separator + "java";

        mojo = nonNull(aMojo);
        mainClassName = nonNull(aMainClassName);
        env = new ArrayList<String>();
        args = new ArrayList<String>();
        jvmArgs = new ArrayList<String>();
        classpath = new ArrayList<String>();
        for (String key : System.getenv().keySet()) {
            env.add(key + "=" + System.getenv(key));
        }
    }

    public JavaCommand addEnvVar(String key, String value) {
        env.add(nonNull(key) + "=" + nonNull(value));
        return this;
    }

    public JavaCommand addJvmArgs(String... args) {
    	if (args == null) {
    		return this;
    	}
        return addJvmArgs(asList(args));
    }

    public JavaCommand addJvmArgs(List<String> args) {
   		this.jvmArgs.addAll(nonNull(args));
        return this;
    }

    public JavaCommand addClasspath(String... classpath) {
    	if (classpath == null) {
    		return this;
    	}
        return addClasspath(asList(classpath));
    }

    public JavaCommand addClasspath(List<String> classpath) {
       	this.classpath.addAll(nonNull(classpath));
        return this;
    }

    public JavaCommand addArgs(String... args) {
    	if (args == null) {
    		return this;
    	}
        return addArgs(asList(args));
    }

    public JavaCommand addArgs(List<String> args) {
       	this.args.addAll(nonNull(args));
        return this;
    }

    public void run(boolean displayCommand) throws Exception {

        List<String> command = buildCommand();
        if (displayCommand) {
            mojo.getLog().info("command: " + " " + join(command.iterator(), " "));
        } else if (mojo.getLog().isDebugEnabled()) {
            mojo.getLog().debug("command: " + " " + join(command.iterator(), " "));
        }

        ProcessBuilder pBuilder = new ProcessBuilder(command);
        Process process = pBuilder.start();
        (new StreamConnector(process.getInputStream())).connectTo(System.out);
        (new StreamConnector(process.getErrorStream())).connectTo(System.err);

        final int exitValue = process.waitFor();
        if (exitValue != 0) {
            throw new MojoFailureException("command line returned non-zero value: " + exitValue);
        }
    }

    private List<String> buildCommand() {
        final ArrayList<String> command = new ArrayList<String>();
        command.add(javaExec);
        command.addAll(jvmArgs);
        command.add("-classpath");
        command.add(join(classpath.iterator(), File.pathSeparator));
        command.add(mainClassName);
        command.addAll(args);
        return command;
    }

    private <T> T nonNull(T object) {
    	if (object == null) {
    		throw new NullPointerException();
    	}
    	return object;
    }

}
